﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetadataBasedConfig.Framework.Model;
using MetadataBasedConfig.EntityModel.Entity;
using MetadataBasedConfig.Framework;
using MetadataBasedConfig.Framework.Validator;
using System.Xml;
using System.Data.Objects.DataClasses;

namespace MetadataBasedConfig.EntityModel
{
    public partial class MDAEntities
    {
        public void LoadConfigInfoDatas(ConfigurableEntityObject configurableEntity)
        {
            IConfigurable configurable = (IConfigurable)configurableEntity;
            IConfigMetadataProvider configMetadataProvider = new EntityConfigMetadataProvider(this); 
            List<KeyValuePair<String, Object>> extendProperties = new List<KeyValuePair<String, Object>>();

            // Load ConfigMeta Information
            String configTypeName = Constants.ConfigTypeName_Table;
            IConfigMetaType configType = configMetadataProvider.CreateMetadata<string, ConfigType>(configTypeName);
            if (configType == null)
                return;

            string mappingTableName = Helper.GetMappingTable(this.GetType());
            var configMeta = configMetadataProvider.GetConfigMeta(configType.ModelKey, mappingTableName);
            if (configMeta == null)
                return; 

            var allConfigInfos = configMetadataProvider
                                       .GetConfigInfos(configMeta.ModelKey)
                                       .IncludeStaticConstraint();
            if (allConfigInfos.Count() <= 0)
                return; 

            // Get the ConfigInfoData from each configInfo, we could check if every configInfo matches with one configInfodata at most.
            // Or we could get the datas using store procedure, but should also check the matching role.
            var allConfigInfoDatas = new List<IConfigMetaConfigInfoData>();
            foreach (var configInfo in allConfigInfos)
            {
                var childDatas = configMetadataProvider
                                       .GetConfigMetaConfigDatasOfMetaInfo(configInfo.ModelKey, configurable.RecID)
                                       .IncludeStaticConstraint();
                if (childDatas.Count() > 1)
                    throw new InvalidOperationException();

                if (childDatas.FirstOrDefault() != null)
                {
                    allConfigInfoDatas.Add(childDatas.First());
                }
            } 

            foreach (var configInfo in allConfigInfos)
            {
                var configData = allConfigInfoDatas.IncludeStaticConstraint().Where(c => c.ConfigInfo.ModelKey == configInfo.ModelKey).FirstOrDefault();
                string configDataValue = configData == null ? string.Empty : configData.SourceValue;
                ConfigInfoPropertyValidatContext context = new ConfigInfoPropertyValidatContext(configDataValue, SourceType.DB);
                foreach (var configInfoProperty in configInfo.ConfigInfoProperties.IncludeStaticConstraint())
                {
                    ((IConfigMetaConfigInfoProperty)configInfoProperty).Validate(context);
                }
                object targetValue = context.IsValid ? context.TargetValue : configDataValue;
                extendProperties.Add(new KeyValuePair<String, Object>(configInfo.Content, context.TargetValue));
            }
            configurableEntity.SetConfigInfoDatas(configMeta, allConfigInfos, allConfigInfoDatas, extendProperties);
        }
    }

    public partial class ConfigMeta : IConfigMeta
    {
        #region Implement IConfigMeta Interface

        IConfigMetaType IConfigMeta.ConfigType
        {
            get
            {
                return this.ConfigType1;
            }
            set
            {
                this.ConfigType1 = (ConfigType)value;
            }
        }

        string IConfigMeta.Name
        {
            get
            {
                return this.Name;
            }
            set
            {
                this.Name = value;
            }
        }

        string IConfigMeta.Content
        {
            get
            {
                return this.Content;
            }
            set
            {
                this.Content = value;
            }
        }

        string IConfigMeta.DataKey
        {
            get
            {
                return this.DataKey;
            }
            set
            {
                this.DataKey = value;
            }
        }

        long IDataKeyModel<long>.ModelKey
        {
            get
            {
                return this.ID;
            }
            set
            {
                this.ID = value;
            }
        }

        IDataKeyModel<long> IDataKeyModel<long>.GetModel(long key)
        {
            ConfigMeta configMeta = GetModel(key, Helper.MDAEntities);
            Helper.MDAEntities.Detach(configMeta);
            return configMeta;
        }

        #endregion

        #region Other Method

        public ConfigMeta GetModel(long key, MDAEntities entities)
        {
            var all = (from c in entities.ConfigMetas where c.ID == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigMetas"));
            return all.FirstOrDefault();
        }

        #endregion

    }

    public partial class ConfigType : IConfigMetaType
    {
        #region Implement IConfigMetaType

        IConfigMetaProcessor IConfigMetaType.ConfigMetaProcessor
        {
            get;
            set;
        }

        string IDataKeyModel<string>.ModelKey
        {
            get
            {
                return this.Name;
            }
            set
            {
                this.Name = value;
            }
        }

        IDataKeyModel<string> IDataKeyModel<string>.GetModel(string key)
        {
            var model = GetModel(key, Helper.MDAEntities);
            Helper.MDAEntities.Detach(model);
            return model;
        }

        #endregion
        
        #region Other Method

        public ConfigType GetModel(string key, MDAEntities entities)
        { 
            var all =  (from c in entities.ConfigTypes where c.Name == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigTypes"));
            return all.FirstOrDefault();
        }

        #endregion
    }

    public partial class ConfigMetaConfigData : IConfigMetaConfigInfoData
    {
        #region Implement IConfigMetaConfigInfoData

        IConfigMetaConfigInfo IConfigMetaConfigInfoData.ConfigInfo
        {
            get
            {
                return this.ConfigMetaConfigInfo;
            }
            set
            {
                this.ConfigMetaConfigInfo = (ConfigMetaConfigInfo)value;
            }
        }

        private object __DataValue = null;
        object IConfigMetaConfigInfoData.DataValue
        {
            get
            {
                if (__DataValue == null)
                    __DataValue = UpdateDataValue(_Value);
                return __DataValue;
            }
            set
            {
                SetDataValue(value);
            }
        }

        string IConfigMetaConfigInfoData.SourceValue
        {
            get
            {
                return this.Value;
            }
            set
            {
                this.Value = value;
            }
        }

        long IDataKeyModel<long>.ModelKey
        {
            get
            {
                return this.ID;
            }
            set
            {
                this.ID = value;
            }
        }

        IDataKeyModel<long> IDataKeyModel<long>.GetModel(long key)
        {
            var model = GetModel(key, Helper.MDAEntities); 
            return model;
        }

        #endregion

        #region Other Method

        public ConfigMetaConfigData GetModel(long key, MDAEntities entities)
        {
            var all = (from c in entities.ConfigMetaConfigDatas where c.ID == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigMetaConfigDatas"));
            return all.FirstOrDefault();
        }
         
        protected virtual void SetDataValue(object value)
        {
            IConfigMetaConfigInfoData configInfoData = (IConfigMetaConfigInfoData)this;
            IConfigMetaConfigInfo configInfo = configInfoData.ConfigInfo;
            ConfigInfoPropertyValidatContext context = new ConfigInfoPropertyValidatContext(value, SourceType.CLR);
            foreach (var configInfoProperty in configInfo.ConfigInfoProperties)
            {
                configInfoProperty.Validate(context);
            }
            if (context.IsValid)
            {
                __DataValue = context.TargetValue;
                _Value = context.TargetValue.ToString();
            }               
            else
            {
                throw new ArgumentException("value is not valid!");
            }
        }

        protected virtual object UpdateDataValue(string value)
        {
            IConfigMetaConfigInfoData configInfoData = (IConfigMetaConfigInfoData)this;
            IConfigMetaConfigInfo configInfo = configInfoData.ConfigInfo;
            ConfigInfoPropertyValidatContext context = new ConfigInfoPropertyValidatContext(value, SourceType.DB);
            foreach (var configInfoProperty in configInfo.ConfigInfoProperties)
            {
                configInfoProperty.Validate(context);
            }
            return context.IsValid ? context.TargetValue : null;
         
        }

        partial void OnValueChanged()
        {
            __DataValue = UpdateDataValue(_Value);
            _Value = __DataValue.ToString();
        } 

        #endregion
    }

    public partial class ConfigMetaConfigInfo : IConfigMetaConfigInfo
    {
        #region Implement IConfigMetaConfigInfo

        IConfigMeta IConfigMetaConfigInfo.ConfigMeta
        {
            get 
            {
                return this.ConfigMeta1;
            }
            set
            {
                this.ConfigMeta1 = (ConfigMeta)value;
            }
        }

        IEnumerable<IConfigMetaConfigInfoProperty> IConfigMetaConfigInfo.ConfigInfoProperties
        {
            get
            {
                return this.ConfigMetaConfigInfoProperties;
            }
            set
            {
                if (value == null)
                    this.ConfigMetaConfigInfoProperties = null;
                else
                {
                    this.ConfigMetaConfigInfoProperties = new EntityCollection<ConfigMetaConfigInfoProperty>();
                    foreach (var p in value)
                    {
                        this.ConfigMetaConfigInfoProperties.Add((ConfigMetaConfigInfoProperty)p);
                    }
                }
            }
        }

        long IDataKeyModel<long>.ModelKey
        {
            get
            {
                return this.ID;
            }
            set
            {
                this.ID = value;
            }
        }

        IDataKeyModel<long> IDataKeyModel<long>.GetModel(long key)
        {
            var model = GetModel(key, Helper.MDAEntities);
            Helper.MDAEntities.Detach(model);
            return model;
        }

        void IConfigMetaConfigInfo.SetConfigInfoData(object value, long dataID)
        {
            var allConfigInfoDatas = (from c in ConfigMetaConfigDatas.IncludeStaticConstraint() where c.DataID == dataID && c.ConfigInfo == ID select c).ToList();
            if (allConfigInfoDatas.Count > 1)
                throw new InvalidOperationException("");
            if (allConfigInfoDatas.Count == 0)
            {
                IConfigMetaConfigInfoData configInfoData = Helper.ConfigMetadataProvider.CreateMetadata<IConfigMetaConfigInfoData,ConfigMetaConfigData>();
                configInfoData.ConfigInfo = this;
                configInfoData.DataID = dataID;
                Helper.ConfigMetadataProvider.AddMetadata<IConfigMetaConfigInfoData,ConfigMetaConfigData>(configInfoData);
            }
            (allConfigInfoDatas.First() as IConfigMetaConfigInfoData).DataValue = value;
        }

        object IConfigMetaConfigInfo.GetConfigInfoData(long dataID)
        {
            var allConfigInfoDatas = (from c in ConfigMetaConfigDatas.IncludeStaticConstraint() where c.DataID == dataID && c.ConfigInfo == ID select c).ToList();
            if (allConfigInfoDatas.Count > 1)
                throw new InvalidOperationException("");
            if (allConfigInfoDatas.Count > 0)
                return (allConfigInfoDatas.First() as IConfigMetaConfigInfoData).DataValue;
            return null;
        }

        #endregion

        #region Other Method

        public ConfigMetaConfigInfo GetModel(long key, MDAEntities entities)
        {
            var all = (from c in entities.ConfigMetaConfigInfoes where c.ID == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigMetaConfigInfoes"));
            return all.FirstOrDefault();
        }

        #endregion
         
    }

    public partial class ConfigMetaConfigInfoProperty : IConfigMetaConfigInfoProperty
    {
        #region Implement IConfigMetaConfigInfoProperty

        IConfigMetaConfigInfo IConfigMetaConfigInfoProperty.ConfigInfo
        {
            get
            {
                return this.ConfigMetaConfigInfo;
            }
            set
            {
                this.ConfigMetaConfigInfo = (ConfigMetaConfigInfo)value;
            }
        }

        IConfigInfoPropertyValidator IConfigMetaConfigInfoProperty.ConfigPropertyValidator
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        long IDataKeyModel<long>.ModelKey
        {
            get
            {
                return this.ID;
            }
            set
            {
                this.ID = value;
            }
        }

        void IConfigMetaConfigInfoProperty.Validate(ConfigInfoPropertyValidatContext context)
        {
            IConfigInfoPropertyValidator validator = ((IConfigMetaConfigInfoProperty)this).ConfigPropertyValidator;
            if (validator != null)
            {
                context.Constraint = this;
                validator.Validate(context);
            }
        }

        IDataKeyModel<long> IDataKeyModel<long>.GetModel(long key)
        {
            var model = GetModel(key, Helper.MDAEntities);
            Helper.MDAEntities.Detach(model);
            return model;
        }

        #endregion

        #region Other Method

        public ConfigMetaConfigInfoProperty GetModel(long key, MDAEntities entities)
        {
            var all = (from c in entities.ConfigMetaConfigInfoProperties where c.ID == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigMetaConfigInfoProperties"));
            return all.FirstOrDefault();
        }

        #endregion

    }

    public partial class ConfigMetaRelation : IConfigMetaRelation
    {
        #region Implement IConfigMetaRelation
        IConfigMeta IConfigMetaRelation.MainConfigMeta
        {
            get
            {
                return this.ConfigMeta;
            }
            set
            {
                this.ConfigMeta = (ConfigMeta)value;
            }
        }

       IConfigMeta IConfigMetaRelation.RelatedConfigMeta
        {
            get
            {
                return this.ConfigMeta1;
            }
            set
            {
                this.ConfigMeta1 = (ConfigMeta)value;
            }
        }

       IConfigMetaRelationType IConfigMetaRelation.RelationType
        {
            get
            {
                return this.ConfigMetaRelationType;
            }
            set
            {
                this.ConfigMetaRelationType = (ConfigMetaRelationType)value;
            }
        }
        #endregion
    }

    public partial class ConfigMetaRelationType : IConfigMetaRelationType
    {
        #region Implement IConfigMetaRelationType

        string IDataKeyModel<string>.ModelKey
        {
            get
            {
                return this.Name;
            }
            set
            {
                this.Name = value;
            }
        }

        IDataKeyModel<string> IDataKeyModel<string>.GetModel(string key)
        {
            var model = GetModel(key, Helper.MDAEntities);
            Helper.MDAEntities.Detach(model);
            return model;
        }

        #endregion

        #region Other Method

        public ConfigMetaRelationType GetModel(string key, MDAEntities entities)
        {
            var all = (from c in entities.ConfigMetaRelationTypes where c.Name == key select c).ToList();
            if (all.Count > 1)
                throw new InvalidOperationException(string.Format("Has multiple key:{0} in {1}", key, "ConfigMetaRelationTypes"));
            return all.FirstOrDefault();
        }

        #endregion
    }
}
